using Core.Db.Dtos;
using Core.Db.Helper;
using Core.Db.Models;
using Microsoft.EntityFrameworkCore;

namespace Core.Db.Services;

public class TouristRouteRepository : ITouristRouteRepository
{
    private readonly AppDbContext _dbContext;
    private readonly IProPertyMappingService _proPertyMappingService;

    public TouristRouteRepository(AppDbContext dbContext, IProPertyMappingService proPertyMappingService)
    {
        _dbContext = dbContext;
        _proPertyMappingService = proPertyMappingService;
    }

    public async Task<PaginationList<TouristRoute>> GetTouristRoutesAsync(string keyword, string operatorType, int? ratingValue, int pageSize, int pageNumber, string orderBy)
    {
        IQueryable<TouristRoute> result = _dbContext.TouristRoutes.Include(x => x.TouristRoutePictures);

        if (string.IsNullOrWhiteSpace(keyword) == false)
        {
            keyword = keyword.Trim();
            result = result.Where(x => x.Title.Contains(keyword));
        }

        if (ratingValue >= 0)
        {
            result = operatorType switch
            {
                "largerThan" => result.Where(x => x.Rating >= ratingValue),
                "lessThan" => result.Where(x => x.Rating <= ratingValue),
                _ => result.Where(x => x.Rating == ratingValue)
            };
        }

        if (string.IsNullOrWhiteSpace(orderBy) == false)
        {
            var touristRouteMappingDictionary = _proPertyMappingService.GetPropertyMapping<TouristRouteDto, TouristRoute>();
            result = result.ApplySort(orderBy, touristRouteMappingDictionary);
        }

        return await PaginationList<TouristRoute>.CreateAsync(pageNumber, pageSize, result);
    }

    public async Task<TouristRoute> GetTouristRouteAsync(Guid touristRouteId)
    {
        return await _dbContext.TouristRoutes.Include(x => x.TouristRoutePictures).FirstOrDefaultAsync(x => x.Id == touristRouteId);
    }

    public async Task<bool> TouristRouteExistAsync(Guid touristRouteId)
    {
        return await _dbContext.TouristRoutes.AnyAsync(x => x.Id == touristRouteId);
    }

    public async Task<IEnumerable<TouristRoutePicture>> GetPicturesByTouristRouteIdAsync(Guid touristRouteId)
    {
        return await _dbContext.TouristRoutePictures.Where(p => p.TouristRouteId == touristRouteId)
            .ToListAsync();
    }

    public async Task<TouristRoutePicture> GetPictureAsync(int pictureId)
    {
        return await _dbContext.TouristRoutePictures.FirstOrDefaultAsync(x => x.Id == pictureId);
    }

    public async Task<IEnumerable<TouristRoute>> GetTouristRouteByIDListAsync(IEnumerable<Guid> ids)
    {
        return await _dbContext.TouristRoutes.Where(x => ids.Contains(x.Id)).ToListAsync();
    }

    public async Task AddTouristRouteAsync(TouristRoute touristRoute)
    {
        if (touristRoute == null)
        {
            throw new ArgumentNullException(nameof(touristRoute));
        }

        _dbContext.TouristRoutes.Add(touristRoute);
        // return Task.CompletedTask;
    }

    public async Task AddTouristRoutePictureAsync(Guid touristRouteId, TouristRoutePicture touristRoutePicture)
    {
        if (touristRouteId == Guid.Empty)
        {
            throw new ArgumentNullException(nameof(touristRouteId));
        }

        if (touristRoutePicture == null)
        {
            throw new ArgumentNullException(nameof(touristRoutePicture));
        }

        touristRoutePicture.TouristRouteId = touristRouteId;
        _dbContext.TouristRoutePictures.Add(touristRoutePicture);
        // return Task.CompletedTask;
    }

    public async Task DeleteTouristRouteAsync(TouristRoute touristRoute)
    {
        _dbContext.TouristRoutes.Remove(touristRoute);
        // return Task.CompletedTask;
    }

    public async Task DeleteTouristRoutesAsync(IEnumerable<TouristRoute> touristRoutes)
    {
        _dbContext.TouristRoutes.RemoveRange(touristRoutes);
        // return Task.CompletedTask;
    }

    public async Task DeleteTouristRoutePictureAsync(TouristRoutePicture touristRoutePicture)
    {
        _dbContext.TouristRoutePictures.Remove(touristRoutePicture);
        // return Task.CompletedTask;
    }

    public async Task<ShoppingCart> GetShoppingCartByUserId(string userId)
    {
        return await _dbContext.ShoppingCarts
            .Include(x => x.User)
            .Include(x => x.ShoppingCartItems)
            .ThenInclude(x => x.TouristRoute)
            .Where(x => x.UserId.Equals(userId))
            .FirstOrDefaultAsync() ?? throw new InvalidOperationException();
    }

    public async Task CreateShoppingCart(ShoppingCart shoppingCart)
    {
        await _dbContext.ShoppingCarts.AddAsync(shoppingCart);
    }

    public async Task AddShoppingCartItem(LineItem lineItem)
    {
        await _dbContext.LineItems.AddAsync(lineItem);
    }

    public async Task<LineItem> GetShoppingCartItemByItemId(int lineItemId)
    {
        return await _dbContext.LineItems.Where(x => x.Id == lineItemId).FirstOrDefaultAsync();
    }

    public async Task DeleteShoppingCartItem(LineItem lineItem)
    {
        _dbContext.LineItems.Remove(lineItem);
    }

    public async Task<IEnumerable<LineItem>> GetShoppingCartsByIdListAsync(IEnumerable<int> ids)
    {
        return await _dbContext.LineItems.Where(x => ids.Contains(x.Id)).ToListAsync();
    }

    public async Task DeleteShoppingCartItems(IEnumerable<LineItem> lineItems)
    {
        _dbContext.LineItems.RemoveRange(lineItems);
    }

    public async Task AddOrderAsync(Order order)
    {
        await _dbContext.Orders.AddAsync(order);
    }

    public async Task<PaginationList<Order>> GetOrdersByUserId(string userId, int pageSize, int pageNumber)
    {
        // return await _dbContext.Orders.Where(x => x.UserId == userId).ToListAsync();
        IQueryable<Order> result = _dbContext.Orders.Where(x => x.UserId == userId);
        return await PaginationList<Order>.CreateAsync(pageNumber, pageSize, result);
    }

    public async Task<Order> GetOrderById(Guid orderId)
    {
        return await _dbContext.Orders.Include(o => o.OrderItems).ThenInclude(oi => oi.TouristRoute)
            .Where(o => o.Id == orderId)
            .FirstOrDefaultAsync();
    }


    public async Task<bool> SaveAsync()
    {
        return await _dbContext.SaveChangesAsync() > 0;
    }
}